import tkinter as tk
from tkinter import ttk, filedialog, scrolledtext
import threading
import os
import time
import socket
from pathlib import Path
import logging

logger = logging.getLogger(__name__)


class FileUpload:
    def __init__(self, main_window):
        self.main_window = main_window
        self.upload_text = None
        self.target_dir_entry = None

    def create_upload_frame(self, parent_frame):
        """创建上传区域框架"""
        upload_frame = ttk.LabelFrame(parent_frame, text="文件上传", padding="5 5 5 5")
        upload_frame.pack(fill=tk.X, padx=10, pady=5)
        upload_frame.grid_columnconfigure(1, weight=1)
        upload_frame.configure(height=220)

        ttk.Label(upload_frame, text="文件/文件夹:").grid(row=0, column=0, padx=5, pady=5, sticky="e")
        self.upload_text = scrolledtext.ScrolledText(upload_frame, height=8, wrap=tk.WORD)
        self.upload_text.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
        button_frame = ttk.Frame(upload_frame)
        button_frame.grid(row=0, column=2, padx=5, pady=5, sticky="ns")
        ttk.Button(button_frame, text="浏览文件", command=lambda: self.browse_upload(True)).pack(side=tk.TOP, fill=tk.X, pady=2)
        ttk.Button(button_frame, text="浏览文件夹", command=lambda: self.browse_upload(False)).pack(side=tk.TOP, fill=tk.X, pady=2)
        ttk.Label(upload_frame, text="远程上传目录:").grid(row=1, column=0, padx=5, pady=5, sticky="e")
        self.target_dir_entry = ttk.Entry(upload_frame, textvariable=self.main_window.target_dir)
        self.target_dir_entry.grid(row=1, column=1, padx=5, pady=5, sticky="ew")
        ttk.Button(upload_frame, text="上传", command=self.upload_files).grid(row=1, column=2, padx=5, pady=5, sticky="ew")

    def browse_upload(self, is_file):
        """浏览上传文件或文件夹"""
        if is_file:
            file_paths = filedialog.askopenfilenames(title="选择文件")
            if file_paths:
                self.upload_text.delete(1.0, tk.END)
                self.upload_text.insert(tk.END, "\n".join(file_paths))
        else:
            folder_path = filedialog.askdirectory(title="选择文件夹")
            if folder_path:
                self.upload_text.delete(1.0, tk.END)
                self.upload_text.insert(tk.END, folder_path)

    def upload_files(self):
        """上传文件到选中的服务器"""
        logger.info("尝试上传文件...")
        # 获取选中的服务器
        selected_servers = []
        for item in self.main_window.server_connection.server_table.get_children():
            values = self.main_window.server_connection.server_table.item(item, "values")
            if values[0] == "√" and values[5] in ["已连接", "已连接(develop)", "已连接(缓存)"]:
                ip = values[1]
                if ip in self.main_window.server_connection.ssh_sessions:
                    selected_servers.append({
                        'ip': ip,
                        'item_id': item
                    })

        if not selected_servers:
            self.main_window.show_message("请选择已连接的服务器")
            logger.warning("上传文件：没有选中的已连接服务器")
            return

        # 获取上传路径和目标目录
        paths_text = self.upload_text.get(1.0, tk.END).strip()
        if not paths_text:
            self.main_window.show_message("请选择要上传的文件或文件夹")
            logger.warning("上传文件：未选择文件或文件夹")
            return

        # 获取上传路径和目标目录
        target_dir = self.main_window.target_dir.get()  # 必须使用get()方法获取实际值
        if not target_dir or target_dir == "/" or target_dir == "":
            self.main_window.show_message("请输入目标目录")
            logger.warning(f"上传文件：目标目录无效: \"{target_dir}\"")
            return

        # 分割多个路径
        paths = [p.strip() for p in paths_text.split('\n') if p.strip()]
        logger.info(f"准备上传以下路径到 {target_dir}: {paths}")

        # 显示开始上传信息
        self.main_window.show_message(f"开始向 {len(selected_servers)} 个服务器上传 {len(paths)} 个路径")
        logger.info(f"开始向 {len(selected_servers)} 个服务器上传 {len(paths)} 个路径")

        # 多线程上传文件
        for server in selected_servers:
            threading.Thread(target=self.upload_to_server,
                             args=(server['ip'], server['item_id'], paths, target_dir)).start()

    def upload_directory(self, ip, item_id, sftp, local_dir, remote_dir):
        """使用 SFTP 批量上传文件和目录和文件，确保正确创建目录结构"""

        def ensure_remote_dir(remote_path):
            """递归创建远程目录"""
            try:
                sftp.stat(remote_path)
                logger.debug(f"远程目录 {remote_path} 已存在")
            except FileNotFoundError:
                logger.info(f"创建远程目录: {remote_path}")
                # 先创建父目录
                parent = os.path.dirname(remote_path)
                if parent and parent != '/':
                    ensure_remote_dir(parent)
                # 创建当前目录
                try:
                    sftp.mkdir(remote_path)
                    logger.debug(f"远程目录 {remote_path} 创建成功")
                except IOError as e:
                    logger.warning(f"创建远程目录 {remote_path} 失败或已存在: {e}")
                    pass  # 目录可能已被其他线程创建

        # 确保本地目录存在
        if not os.path.exists(local_dir):
            logger.error(f"本地目录不存在: {local_dir}")
            self.main_window.show_message(f"本地目录不存在: {local_dir}")
            print(f"本地目录不存在: {local_dir}")
            return 0

        # 确保远程根目录存在
        ensure_remote_dir(remote_dir)

        total_files = 0
        uploaded_files = 0

        # 计算文件总数
        file_count = sum([len(files) for _, _, files in os.walk(local_dir)])
        total_files += file_count
        logger.info(f"开始上传目录 {local_dir} 到 {ip}:{remote_dir}, 共 {total_files} 个文件")

        # 遍历本地目录
        for root, dirs, files in os.walk(local_dir):
            # 计算相对路径并替换分隔符
            rel_path = os.path.relpath(root, local_dir).replace(os.sep, '/')

            if rel_path == '.':
                current_remote_dir = remote_dir
            else:
                # 使用统一的正斜杠拼接路径
                current_remote_dir = f"{remote_dir}/{rel_path}"
                ensure_remote_dir(current_remote_dir)

            # 上传文件
            for file in files:
                local_file = os.path.join(root, file)
                # 使用正斜杠构造远程路径
                remote_file = f"{current_remote_dir}/{file}"
                try:
                    status_msg = f"上传: {file} ({uploaded_files + 1}/{total_files})"
                    self.main_window.server_connection.update_server_status(item_id, status_msg, 8)
                    self.main_window.server_connection.append_to_terminal(ip, f"上传: {local_file} -> {remote_file}\n")
                    logger.info(f"上传文件: {local_file} -> {ip}:{remote_file}")
                    sftp.put(local_file, remote_file)
                    uploaded_files += 1
                except Exception as e:
                    err_msg = f"上传 {local_file} 失败: {str(e)}"
                    self.main_window.show_message(f"上传 {local_file} 到 {ip} 失败: {str(e)}")
                    self.main_window.server_connection.append_to_terminal(ip, f"错误: {err_msg}\n")
                    logger.error(f"上传文件 {local_file} 到 {ip}:{remote_file} 失败: {e}", exc_info=True)

        final_msg = f"已上传 {uploaded_files}/{total_files} 个文件"
        self.main_window.server_connection.update_server_status(item_id, final_msg, 8)
        self.main_window.server_connection.append_to_terminal(ip, f"\n{final_msg}\n")
        logger.info(f"目录上传完成: {ip} - {final_msg}")
        
        return uploaded_files

    def upload_to_server(self, ip, item_id, paths, target_dir):
        """上传文件到指定服务器"""
        ssh = self.main_window.server_connection.ssh_sessions[ip]['ssh']
        shell = self.main_window.server_connection.ssh_sessions[ip]['shell']

        try:
            # 更新上传状态
            self.main_window.server_connection.update_server_status(item_id, "准备上传...", 8)
            self.main_window.server_connection.append_to_terminal(ip, f"\n开始上传文件到 {target_dir}\n")
            logger.info(f"开始上传文件到 {ip}:{target_dir}")

            # 创建SFTP客户端
            sftp = ssh.open_sftp()
            logger.info(f"已创建SFTP客户端到 {ip}")

            # 确保目标目录存在 (通过shell执行mkdir命令)
            shell.send(f"mkdir -p {target_dir}\n")
            time.sleep(1)
            try:
                shell.recv(4096, timeout=5)  # 清空输出缓冲区，设置5秒超时
            except socket.timeout:
                logger.warning(f"清空输出缓冲区超时，继续执行: {ip}")
            except Exception as e:
                logger.warning(f"清空输出缓冲区时出现异常: {e}")
            logger.info(f"在 {ip} 上确保目标目录 {target_dir} 存在")

            total_files = 0
            uploaded_files = 0

            # 遍历所有路径
            for path in paths:
                path_obj = Path(path)
                if path_obj.is_file():
                    # 上传单个文件
                    path = str(path_obj).replace('/', os.sep)
                    total_files += 1
                    remote_path = f"{target_dir}/{path_obj.name}"
                    status_msg = f"上传: {path_obj.name} (1/{total_files})"
                    self.main_window.server_connection.update_server_status(item_id, status_msg, 8)
                    self.main_window.server_connection.append_to_terminal(ip, f"上传: {path_obj} -> {remote_path}\n")
                    logger.info(f"上传文件: {path_obj} -> {ip}:{remote_path}")
                    try:
                        sftp.put(path, remote_path)
                        uploaded_files += 1
                    except Exception as e:
                        err_msg = f"上传 {path} 失败: {str(e)}"
                        self.main_window.show_message(f"上传 {path} 到 {ip} 失败: {str(e)}")
                        self.main_window.server_connection.append_to_terminal(ip, f"错误: {err_msg}\n")
                        logger.error(f"上传文件 {path} 到 {ip}:{remote_path} 失败: {e}", exc_info=True)

                elif path_obj.is_dir():
                    # 确保路径使用系统正确的分隔符
                    path = str(path_obj).replace('/', os.sep)
                    logger.info(f"开始上传目录 {path} 到 {ip}:{target_dir}")

                    dir_uploaded_files = self.upload_directory(ip, item_id, sftp, path, target_dir)
                    if dir_uploaded_files is not None:
                        uploaded_files += dir_uploaded_files

            # 上传完成后的最终状态更新
            if uploaded_files > 0:
                final_status = f"上传完成({uploaded_files}个文件)"
                self.main_window.server_connection.update_server_status(item_id, final_status, 8)
                self.main_window.server_connection.append_to_terminal(ip, f"\n=== 上传完成 ===\n")
                self.main_window.server_connection.append_to_terminal(ip, f"总共上传了 {uploaded_files} 个文件到 {target_dir}\n")
                logger.info(f"上传完成: {ip} - {final_status}")
            else:
                final_status = "上传失败(无文件上传)"
                self.main_window.server_connection.update_server_status(item_id, final_status, 8)
                self.main_window.server_connection.append_to_terminal(ip, f"\n=== 上传失败 ===\n")
                self.main_window.server_connection.append_to_terminal(ip, f"没有成功上传任何文件\n")
                logger.warning(f"上传失败: {ip} - {final_status}")

            sftp.close()
            logger.info(f"SFTP客户端到 {ip} 已关闭")

        except Exception as e:
            err_msg = f"上传失败: {str(e)}"
            self.main_window.server_connection.update_server_status(item_id, err_msg, 8)
            self.main_window.server_connection.append_to_terminal(ip, f"错误: {err_msg}\n")
            logger.error(f"上传失败: {e}", exc_info=True) 